/*

DAT2MINIX.C (C) Copyright Bill Buckels 2008.
All rights reserved.

Written by   : Bill Buckels
Email        : bbuckels@mts.net

Date Written : May 2008
Updated      : June 2008 - RAG and RAX exports added

Environment  : MSC (Microsoft C) and the Visual Studio 2005 Command Line
               with the Win32 SDK.

               I have made abolutely no attempt to make this program
               compatible with any C compiler outside the MSC environment
               stated above. In this environment many of the functions
               are deprecated because they are insecure unless used in
               a CRT application. My MAKEFILE should be reviewed as to
               how I build this, and I should also note that the correct
               platform SDK has been used for Windows XP to build this so
               as with anything, results may vary and you are on your own
               when it comes to porting this to other OS's and compilers.

               Having said that, it would be very simple for me to write
               an Aztec C version of this that would run in ProDOS or
               even on a Commodore 64 or in CPM/80 or CPM/86 and I might
               even be able to cook-up a Macintosh or an Amiga version
               if was so inclined having already done all the major work
               in writing this puppy in the first place, so I know that
               other folks that are at least as smart as I am won't have
               much trouble porting this if they want a version that runs
               on a different OS. 'Nuff said.

Purpose      : Legacy Graphics Converter

               This program converts IBM format Old PrintShop .DAT files
			   to Apple II BSaved Images with a compatible layout for the
			   ProDOS version of the Beagle Bros Minipix progarm in a
			   subdirectory of the same basename as the .DAT file.

			   The IBM format Old PrintShop .DAT file is paired with a
			   .NAM file of pic names, but may not always be present
			   during conversion.

			   If both the .DAT and .NAM file are present during the
			   conversion, it also produces individual Minipix using the
			   captions in the ,NAM file and automatic naming. These are
			   compatible with both the DOS 3.3 and ProDOS versions of
			   Minipix, and with other programs like the Old PrintShop
			   for the Apple II.

               It was written with several purposes in mind and in fact
			   the primary purpose was to reconstruct ProDOS Minipix
			   disks 2 and 3 from IBM DAT files that I had converted in
			   the early 1990's from the original Apple II BSaved Images
			   after porting these from the Apple II to the IBM-PC.

               The original disks that I had were long ago lost. Recently
               however I was fortunate in obtaining an Apple II Emulator
               DiskImage of Disk 1 in this series which offered me a
               template for reconstruction of the other two disks in
               the 3 disk series.

               Building further on this approach, I realized that it
               would also be possible to create additional MiniPix
               DiskImages from the many IBM Olde PrintShop DAT files
               that I have accumulated over the years and now offer
               with my ClipShop program.

               So for what it's worth that's why I wrote this converter
               and I intend to incorporate it into the ClipShop program in
               a minor release to my ClipShop website if I haven't already
               done so by the time you read this. But only after I finish
               recreating the Minipix disks that I originally set-out to do.

               That's quite enough on the matter.

               June 2008 - Well almost.

               As an afterthought and because I needed to convert some old
               printshop graphics for use in my metoo program for the
               Apple II I added a RAG and RAX file export.

Licence Agreement
-----------------

All my work is copyrighted and belongs to me. This program is not
derived from anything by anyone and is my own work in its entirety.

I herewith grant you a non-exclusive and conditional licence to use this
source code and the output files it produces for whatever use you deem
fit, provided you do not take credit for my work, and that you leave my
copyright notices intact in all of it.

If you augment or otherwise use my work you must always also include your
own personal copyright notice but it may never be a GNU public licence or
anything else that resembles fascism or totalitarianism and world-domination
or a commercial or educational licence either. You can use my stuff
commercially or for GNU with my conditions intact if they let you (they
should since copyright is for authors and the public and I belong to both
groups) but you must never copyright my work with any company copyright
whatsoever; just your own personal copyright like mine and leave mine in
place. That is the way copyright is intended to work and that is the way
that it will work with my stuff unless I selectively decide otherwise.

In addition you must agree that I am not liable in any way shape or form for
any damage from the use of any of this in any way whatsoever.

If you do not agree with all of the aforementioned conditions of use or if
your use is not Fair then remove all of this from your computer now.

Bill Buckels
bbuckels@mts.net
May 2008

*/

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys\stat.h>
#include <string.h>

#define PSHOP_CHUNK 572
#define SCREEN_SIZE 8192
#define MAX_IMAGE 22

/* 2 byte header + 13 byte width x 52 rasters */
#define RAG_SIZE 678


/* a couple of buffers */
char BSavedImage[SCREEN_SIZE];
char MiniPic[PSHOP_CHUNK];
char RagBuffer[RAG_SIZE];

unsigned char msk[]={0x80,0x40,0x20,0x10,0x8,0x4,0x2,0x1};

int pixperset = 9;
int set = 64;

/* the Hi-RES BASE offsets for Apple II Binary Image and Screen format */
/* rather than calculate the double interleaved rasters
   I find it more convenient to us a table for the start of each. */
unsigned int HB[] = {
0, 1024,2048,3072,4096,5120,6144,7168,128,
1152,2176,3200,4224,5248,6272,7296,256,
1280,2304,3328,4352,5376,6400,7424,384,
1408,2432,3456,4480,5504,6528,7552,512,
1536,2560,3584,4608,5632,6656,7680,640,
1664,2688,3712,4736,5760,6784,7808,768,
1792,2816,3840,4864,5888,6912,7936,896,
1920,2944,3968,4992,6016,7040,8064,40,
1064,2088,3112,4136,5160,6184,7208,168,
1192,2216,3240,4264,5288,6312,7336,296,
1320,2344,3368,4392,5416,6440,7464,424,
1448,2472,3496,4520,5544,6568,7592,552,
1576,2600,3624,4648,5672,6696,7720,680,
1704,2728,3752,4776,5800,6824,7848,808,
1832,2856,3880,4904,5928,6952,7976,936,
1960,2984,4008,5032,6056,7080,8104,80,
1104,2128,3152,4176,5200,6224,7248,208,
1232,2256,3280,4304,5328,6352,7376,336,
1360,2384,3408,4432,5456,6480,7504,464,
1488,2512,3536,4560,5584,6608,7632,592,
1616,2640,3664,4688,5712,6736,7760,720,
1744,2768,3792,4816,5840,6864,7888,848,
1872,2896,3920,4944,5968,6992,8016,976,
2000,3024,4048,5072,6096,7120,8144};

/* since a raster is 40 bytes in length and each byte
   holds only 7 pixels and 1 palette bit, some trickery
   is involved in setting the pixels correctly from the
   minipix in the IBM DAT file since these hold 8 pixels
   per byte which works out to 88 pixels for each 11 byte raster.

   I have therefore simplified this calculation into two parts:
   		1. Get the raster byte that the logical pixel is in.
   		2. Get the bit position for that pixel.

   Doing it this way and then using an inclusive OR to
   set the pixel at the appropriate position in the byte
   array for the output file greatly facilitates readability
   and makes this whole affair much easier to code.

   */


/* x,y coordinates for the placement of
   minipix positions 1-9 in the BSaved Image */
int pos[9][2] = { 0,0,    91,0,    182,0,
                  0,54,   91,54,   182,54,
                  0,108,  91,108,  182,108};

int getpixelbyte(int x, int picpos)
{
	int x1;

	/* range check */
	if (picpos < 0 || picpos > 8)picpos = 0;
	if (x < 0 || x > 87)x = 0;

	x1 = (pos[picpos][0] + x)/7;
	return x1;
}

int getpixelbit(int x, int picpos)
{
	int x1;

	/* range check */
	if (picpos < 0 || picpos > 8)picpos = 0;
	if (x < 0 || x > 87)x = 0;

	x1 = (pos[picpos][0] + x)%7;
	return x1;
}

int clearimage()
{
	int x, y, temp;
	unsigned char *raster;

	/* set the background to black */
	memset(BSavedImage,0,SCREEN_SIZE);
	/* Minipix uses the Orange and Blue palette
	  so set the palette for each raster...
	  otherwise the pallete will be Green and Violet */
	for (y = 0; y < 192; y++) {
	   temp = HB[y];
	   raster = (unsigned char *)&BSavedImage[temp];
	   for (x = 0; x < 40; x++)raster[x] = 0x80;
	}
	return 0;
}

int putpic(int picpos)
{
	unsigned char c;
	int temp,x,x1,y,y1,offset,byteoff,bitoff,idx;
	unsigned char *raster;

    /* set the background to black on the first minipic */
    if (picpos == 0)clearimage();

    /* get the logical raster origin for the minipix position
       in the BSaved Image */
	y1 = pos[picpos][1];

	offset = 0;
	/* yterm */
	for (y = 0; y < 52; y++, y1++) {
		/* get the start of the raster in the BSaved Image
		   and point to it */
		temp = (int)HB[y1];
		raster = (unsigned char *)&BSavedImage[temp];
	    /* xterm */
	    x = 0;
	    for (x1 = 0; x1 < 11; x1++) {
	        /* get the next byte in the MiniPic */
	        c = (unsigned char)MiniPic[offset];
			offset++;
            /* set each pixel in the BSaved Image
               by translating from IBM monochrome graphics
               to Apple II monochrome graphics...
               this amounts to reducing from 8 pixels to 7 pixels
               per byte and making the appropriate adjustment
               in memory. */
			for (idx = 0; idx < 8; idx++) {
				/* the image in memory was cleared when we started
				   and the palette bits set for Orange Blue which
				   is the palette used by Beagle Bros. for Minipix */
				/* since all pixels are initially black we only need
				   to set the white pixels... */
				if ( ! (c&msk[idx])) {
					byteoff = getpixelbyte(x,picpos);
					bitoff = getpixelbit(x,picpos);
					raster[byteoff] |= (1 << bitoff);
				}
				x++;

			}
		}
	}
    return 0;

}


/* the following uses Beagle Naming faithfully.
   the ProDOS minipix never included SET.J, SET.O, or SET.Z
   also SET.X and SET.Y contained only 6 minipix.
   the others contained 9.

   There were a total of 23 BSaved images distributed
   on Disks 1-3 containing a total of 23 x 9 - 6 = 201 minipix
   despite the fact that the Beagle docs always said 200. */

int getnextset()
{
	char c;

	set++;

	/* no sets are allowed below '0' */
	if (set < 48) return -1;
	/* no sets are allowed between '9' and 'A' */
	if (set > 57 && set < 65) return -1;

	/* after 'Y' use SET.0 through SET.9 */
	/* technically an IBM old printshop DAT file
	   holds fewer minipix than the orginal minipix program
	   provided anyway due to the limitations of the IBM
	   version 1 of printshop which originated these, so
	   it is unlikely that this will ever happen in practice
	   unless someone is using a DAT file created by some
	   other source */
	if (set > 89) set = 48;

	c = (char)set;

	/* skip the characters that minipix does not use */
	if (c == 'I')c = 'J';
	if (c == 'O')c = 'P';
	set = (int)c;

    if (c == 'X' || c == 'Y')pixperset = 6;
    else pixperset = 9;

	return set;

}


/* expand a minipic into a RAG formatted buffer */
/* the minipic width is 88 pixels.
   since rasters are 7 pixels per byte this means
   that 3 pixels will remain unset in the minipic
   for each raster when saving to a byte oriented
   image fragment in the .RAG format which will
   have 13 bytes (91 pixels) per native mode scanline.

 */

int mini2rag()
{
	int c,x,x1,y,y1,offset,byteoff,bitoff,idx;

    /* create the RAG header and set the RAG background to black */
    /* use the orange and blue palette */
    RagBuffer[0] = 13; /* bytes */
    RagBuffer[1] = 52; /* height */
    for (offset = 2; offset < RAG_SIZE; offset++)RagBuffer[offset] = 0x80;

	offset = 0;
	/* yterm */
	for (y = 0; y < 52; y++) {
		/* get the start of the raster in the RAG buffer */
		y1 = (y * 13) + 2;
	    /* xterm */
	    x = 0;
	    for (x1 = 0; x1 < 11; x1++) {
	        /* get the next byte in the MiniPic */
	        c = MiniPic[offset];
			offset++;
            /* set each pixel in the RAG buffer
               by translating from 8 bit monochrome graphics
               to Apple II 7 bit graphics
               this amounts to reducing from 8 pixels to 7 pixels
               per byte */
			for (idx = 0; idx < 8; idx++) {
				if ( c&msk[idx]) {
					byteoff = y1 + (x/7);
					bitoff = x%7;
					RagBuffer[byteoff] |= (1 << bitoff);
				}
				x++;

			}
		}
	}
	/* reverse - white background */
	for (offset = 2; offset < RAG_SIZE; offset++) {
		c = (RagBuffer[offset] ^ 0x7f);
		RagBuffer[offset] = c;
	}


    return 0;

}


/*

The Apple ][ Minipix Format

When The Print Shop first appeared on The Apple ][, the
only type of graphics it used were small 4 sector DOS3.3
files, called minipix. Because the range of them built into
The Print Shop® was limited, people drew their own, using
the print Shop Graphic Editor, and soon these little 88x52
pictures were everywhere. There were disks and disks, just
packed with them. The storage format was quite simple,
basically just a bit mapped image, with every 11 bytes of
the file mapping to one line of 88 dots on the screen. An
extra 4 filler bytes were tacked on the end of each
mini-graphic file. Each file held only 1 mini-graphic. 52
lines x 8 bytes + 4 = 576 bytes. Now when "The New Print
Shop" came along, you could convert these little graphics to
the new format. It turns out that all that was done to each
graphic to convert it, was a small header was placed at the
start, and the 4 filler bytes were dropped from the end, and
so 576 byte graphics became 605 byte "NPS" compatible
graphics.

*/

/* pcx encoder helper */
/* the writer for the encline function */
int encput(unsigned char byt,unsigned char cnt, FILE *pcxfile)
{
          if(cnt){
            if((cnt==1)&& (0xc0 != (0xc0 &byt))){
                if(EOF == fputc((int)byt,pcxfile))
                return(0);
                return(1);
            }
            else{
                if(EOF==fputc((int)0xc0|cnt,pcxfile))
                return(0);
                if(EOF==fputc((int)byt,pcxfile))
                return(0);
                return(2);
            }
        }
        return(0);

}

/* encode a line in pcxformat encoding */
/* encodes a raw line and writes it out to disk */
int encline(unsigned char *inbuff,int inlen, FILE *pcxfile)
{
    unsigned char this,last;
    int srcindex,i;
    int total;
    unsigned char runcount;
    total=0;
    last = *(inbuff); runcount=1;

for(srcindex=1;srcindex!=inlen;srcindex++){
    this= *(++inbuff);
    if(this==last){
        runcount++;
        if(runcount==63){
            if(!(i=encput(last,runcount,pcxfile)))
            return(0);
            total+=i;
            runcount=0;
        }
    }
    else{
        if(runcount){
            if(!(i=encput(last,runcount,pcxfile)))
            return(0);
            total+=i;
        }
        last=this;
        runcount=1;
      }
   }

   if(runcount){
    if(!(i=encput(last,runcount,pcxfile)))
    return(0);
    return(total+i);
    }
    return (total);

}



int main(int argc, char **argv)
{
	FILE *fprax;

	int done = 0, started = 0, fh, fhname, fhdat,
	    fhmini, idx, jdx, cnt = 0, minicnt=0;
	char datname[256], outfile[66], dirname[256],
	     namename[256], caption[17], ragname[17], c;
	/* filler bytes for the end of the Minipic */
    char namefiller[] = { 0xFF, 0xFF, 0xFF, 0xFF};


puts("Dat2Mini(C) Copyright Bill Buckels 2008.\nAll rights reserved.");

    if (argc < 2) {
		puts("Usage is: \"Dat2Mini [old printshop.DAT]\"");
		puts("Output1:   Apple II Bsaved Images for use with ProDOS MiniPix.");
		puts("Output2:   Individual Apple II Minipix Automatically Named.");
		exit(0);
	}

	/* only accept files with the .DAT extension */
	/* this gives some assurance that this may be an IBM old printshop library
	   which is only a sequential file of 88 x 52 monochrome images.
	   avoid the complexity of needing to deal with .POG and .SHP files.
	   */
	strcpy(datname,argv[1]);
	jdx = 999;
	for (idx = 0; datname[idx]!=0; idx++) {
		if (datname[idx] == '.')jdx = idx;
	}
	if (jdx != 999)datname[jdx] = 0;
	strcpy(dirname,datname);
	strcpy(namename,datname);
	strcat(datname,".DAT");
	strcat(namename,".NAM");


	fhdat = _open(datname, O_RDONLY | O_BINARY);

	if (fhdat == -1) {
	  printf("Unable to open %s.\n",datname);
	  exit(1);
	}


    /* if there is a name file create the individual minipix as well.
       these are used by the DOS 3.3 version as well as by the old printshop
       program for the apple */
    fhname = _open(namename, O_RDONLY | O_BINARY);
    caption[16] = 0;

   	for (;;) {

	   /* if we are out of range we are done */
	   if (getnextset() == -1)break;
	   fh = -1;
	   for (idx = 0; idx < pixperset; idx++) {

	       if (_read(fhdat, MiniPic, PSHOP_CHUNK) != PSHOP_CHUNK) {
		      done = 1;
		      break;
		   }

		   if (fh == -1) {

			   if (!started) {
			    /* create in subdirectory if possible */
			       _mkdir(dirname);
    			   _chdir(dirname);
    			   started = 1;
    			   printf("Images will be created in directory: %s\n",dirname);
			   }

			   sprintf(outfile,"SET.%c",(char)set);
			   fh = _open(outfile, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, S_IWRITE);
			   if (fh == -1) {
				   done = 1;
				   break;
			   }

		   }

	       /* build the BSaved Image */
	       /* place the minipic into the required position */
		   putpic(idx);

	       /* if they have a name file create an individual minipic */
		   /* valid apple filenames only upper case Alpha-Numeric
		      names only and replace all other characters with
		      periods so the filename is valid on the Apple II */
		   if (fhname != -1) {
		     if (_read(fhname, caption, 16) == 16) {

				caption[15] = 0; /* ProDOS filename limit */

				/* pattern matching of original DOS 3.3 Minipix */
				/* these are the ones that I converted originally */
				/* I substituted the disk number for the period */
				/* so I reverse this process from my 1990 conversion */
				if (caption[0] == 'P' && caption[1] == 'I' && caption[2] == 'C' &&
				    caption[6] < 33 &&
				    (caption[4] > 64 && caption[4] < 90) &&
				    (caption[5] > 48 && caption[5] < 58)) {
						 caption[3] = '.'; /* replace disk number with dot */
						 caption[6] = 0;   /* truncate trailing white space */
				}
				else {
					/* otherwise I convert the IBM descriptive caption */
					/* on the IBM this could be anything */
			    	for (jdx = 0; jdx < 16; jdx++) {
					  c = toupper(caption[jdx]);
					  if (c == 0) break;
					  if (c > 47 && c < 58) continue;
					  if (c < 65 || c > 90) c = '.';
					  caption[jdx] = c;
					}
				}
				fhmini = _open(caption, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, S_IWRITE);
				if (fhmini != -1) {
					_write(fhmini, MiniPic, PSHOP_CHUNK);
					_write(fhmini, namefiller, 4);
					_close(fhmini);
					minicnt++;
					/* june 2008 - added RAG and RAX exports */
					strcpy(ragname, caption);
					ragname[11] = 0;
					strcat(ragname,".RAG");
					mini2rag();
					fhmini = _open(ragname, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, S_IWRITE);
					if (fhmini != -1) {
						_write(fhmini, RagBuffer, RAG_SIZE);
						_close(fhmini);
					}
					strcpy(ragname, caption);
					ragname[11] = 0;
					strcat(ragname,".RAX");
					mini2rag();
					fprax = fopen(ragname,"wb");
					if (NULL != fprax) {
						/* write the encoded file */
						fputc(RagBuffer[0],fprax);
						fputc(RagBuffer[1],fprax);
						encline((unsigned char *)&RagBuffer[2],RAG_SIZE - 2,fprax);
						fclose(fprax);
					}
				}

			 }
		   }
	   }
	   if (fh != -1) {
		   /* write out the bsaved file after the
		      last image and go do the next
		      if another */
		   _write(fh, BSavedImage, SCREEN_SIZE);
		   _close(fh);
		   /* too messy to display each image name */
		   /* printf("%s Created!\n",outfile); */
		   cnt++;
	   }
	   if (done)break;

	}
	if (started == 1)_chdir("..");
	_close(fhdat);
	if (fhname != -1) {
		_close(fhname);
		printf("Done! %d images created. %d minipix created.\n",cnt,minicnt);
	}
	else {
		printf("Done! %d images created.\n",cnt);
	}
	return 0;
}
